package gsw.tool.pluggable.pm.parser;

import android.content.ComponentName;
import android.content.Context;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.text.TextUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import gsw.tool.pluggable.utils.reflect.ComponentNameComparator;
import gsw.tool.pluggable.utils.reflect.PluginDirHelper;

/**
 * 缓存插件APK的四大组件的信息
 * 四大组件在内存中有三种表现形式：清单文件中的配置信息->存档信息->运行的组件。
 * 为了启动四大组件，需要先缓存其清单文件中的配置信息与存档信息，从而得到实际运行的组件。
 */
public class PackageMapPlugin {
    /**
     * 缓存四大组件的ComponentName与清单文件中的配置信息
     * ComponentName包含组件的包名和全类名
     */
    //缓存Activity在清单文件中的配置信息，对应PackageParser.Activity，跟android.app.Activity相差很远
    private Map<ComponentName, Object> mActivityObjCache = new TreeMap<ComponentName, Object>(new ComponentNameComparator());
    //缓存Servie在清单文件中的配置信息，对应PackageParser.Servie，跟android.app.Servie相差很远
    private Map<ComponentName, Object> mServiceObjCache = new TreeMap<ComponentName, Object>(new ComponentNameComparator());
    //缓存ContentProvider在清单文件中的配置信息，对应PackageParser.Provider，跟android.content.ContentProvider相差很远
    private Map<ComponentName, Object> mProviderObjCache = new TreeMap<ComponentName, Object>(new ComponentNameComparator());
    //缓存BroadcastReceiver在清单文件中的配置信息，对应PackageParser.Activity，因为Activity的结构与BroadcastReceiver相似，所以用同一种对象表示
    private Map<ComponentName, Object> mReceiversObjCache = new TreeMap<ComponentName, Object>(new ComponentNameComparator());

    /**
     * 缓存四大组件的ComponentName与其存档
     */
    private Map<ComponentName, ActivityInfo> mActivityInfoCache = new TreeMap<ComponentName, ActivityInfo>(new ComponentNameComparator());
    private Map<ComponentName, ServiceInfo> mServiceInfoCache = new TreeMap<ComponentName, ServiceInfo>(new ComponentNameComparator());
    private Map<ComponentName, ProviderInfo> mProviderInfoCache = new TreeMap<ComponentName, ProviderInfo>(new ComponentNameComparator());
    private Map<ComponentName, ActivityInfo> mReceiversInfoCache = new TreeMap<ComponentName, ActivityInfo>(new ComponentNameComparator());

    /**
     * 缓存四大组件的ComponentName与清单文件中注册的IntentFilter
     */
    private Map<ComponentName, List<IntentFilter>> mActivityIntentFilterCache = new TreeMap<ComponentName, List<IntentFilter>>(new ComponentNameComparator());
    private Map<ComponentName, List<IntentFilter>> mServiceIntentFilterCache = new TreeMap<ComponentName, List<IntentFilter>>(new ComponentNameComparator());
    private Map<ComponentName, List<IntentFilter>> mProviderIntentFilterCache = new TreeMap<ComponentName, List<IntentFilter>>(new ComponentNameComparator());
    private Map<ComponentName, List<IntentFilter>> mReceiverIntentFilterCache = new TreeMap<ComponentName, List<IntentFilter>>(new ComponentNameComparator());

    //插件APK文件
    private File mPluginFile;
    //自定义的PackageParser
    private PackageParser mParser;
    //插件APK的包名
    private String mPackageName;
    //PMS服务的上下文
    private Context mHostContext;
    //宿主的包名信息
    private PackageInfo mHostPackageInfo;

    /**
     * 初始化数据，使用自定义的PackageParser获取四大组件的信息
     *
     * @param hostContext PMS服务的上下文
     * @param pluginFile  插件APK文件
     */
    public PackageMapPlugin(Context hostContext, File pluginFile) throws Exception {
        mHostContext = hostContext;
        mPluginFile = pluginFile;
        mParser = PackageParserManager.getInstance().getPluginParser(hostContext);
        mParser.parsePackage(pluginFile, 0);
        // 插件的包名
        mPackageName = mParser.getPackageName();
        //宿主的包信息
        mHostPackageInfo = mHostContext.getPackageManager().getPackageInfo(mHostContext.getPackageName(), 0);
        //获取Activity在清单文件中的配置信息
        List datas = mParser.getActivities();
        for (Object activity : datas) {
            //获取Activity的ComponentName
            ComponentName componentName = new ComponentName(mPackageName, mParser.readNameFromComponent(activity));
            //缓存Activity的ComponentName与其存档
            mActivityObjCache.put(componentName, activity);
            //获取Activity的存档信息
            ActivityInfo value = mParser.generateActivityInfo(activity, 0);
            //修复插件APK的ApplicationInfo信息
            fixApplicationInfo(value.applicationInfo);
            if (TextUtils.isEmpty(value.processName)) {
                value.processName = value.packageName;
            }
            //缓存Activity的ComponentName与其存档
            mActivityInfoCache.put(componentName, value);
            List<IntentFilter> filters = mParser.readIntentFilterFromComponent(activity);
            //移除旧的IntentFilter缓存
            mActivityIntentFilterCache.remove(componentName);
            //缓存Activity的ComponentName与清单文件中注册的IntentFilter
            mActivityIntentFilterCache.put(componentName, new ArrayList<IntentFilter>(filters));
        }
        //获取Service在清单文件中的配置信息
        datas = mParser.getServices();
        for (Object data : datas) {
            ComponentName componentName = new ComponentName(mPackageName, mParser.readNameFromComponent(data));
            mServiceObjCache.put(componentName, data);
            ServiceInfo value = mParser.generateServiceInfo(data, 0);
            fixApplicationInfo(value.applicationInfo);
            if (TextUtils.isEmpty(value.processName)) {
                value.processName = value.packageName;
            }
            mServiceInfoCache.put(componentName, value);
            List<IntentFilter> filters = mParser.readIntentFilterFromComponent(data);
            mServiceIntentFilterCache.remove(componentName);
            mServiceIntentFilterCache.put(componentName, new ArrayList<IntentFilter>(filters));
        }
        //获取ContentProvider在清单文件中的配置信息
        datas = mParser.getProviders();
        for (Object data : datas) {
            ComponentName componentName = new ComponentName(mPackageName, mParser.readNameFromComponent(data));
            mProviderObjCache.put(componentName, data);
            ProviderInfo value = mParser.generateProviderInfo(data, 0);
            fixApplicationInfo(value.applicationInfo);
            if (TextUtils.isEmpty(value.processName)) {
                value.processName = value.packageName;
            }
            mProviderInfoCache.put(componentName, value);

            List<IntentFilter> filters = mParser.readIntentFilterFromComponent(data);
            mProviderIntentFilterCache.remove(componentName);
            mProviderIntentFilterCache.put(componentName, new ArrayList<IntentFilter>(filters));
        }
        //获取BroadcastRecevier在清单文件中的配置信息
        datas = mParser.getReceivers();
        for (Object data : datas) {
            ComponentName componentName = new ComponentName(mPackageName, mParser.readNameFromComponent(data));
            mReceiversObjCache.put(componentName, data);

            ActivityInfo value = mParser.generateActivityInfo(data, 0);
            fixApplicationInfo(value.applicationInfo);
            if (TextUtils.isEmpty(value.processName)) {
                value.processName = value.packageName;
            }
            mReceiversInfoCache.put(componentName, value);

            List<IntentFilter> filters = mParser.readIntentFilterFromComponent(data);
            mReceiverIntentFilterCache.remove(componentName);
            mReceiverIntentFilterCache.put(componentName, new ArrayList<IntentFilter>(filters));
        }
    }

    /**
     * 修复插件APK的ApplicationInfo信息
     * 设置ApplicationInfo的sourceDir、publicSourceDir、nativeLibraryDir与processName.
     * 插件的processName设置为它的包名。
     *
     * @param applicationInfo 进程信息
     */
    private ApplicationInfo fixApplicationInfo(ApplicationInfo applicationInfo) {
        if (applicationInfo.sourceDir == null) {
            applicationInfo.sourceDir = mPluginFile.getPath();
        }
        if (applicationInfo.publicSourceDir == null) {
            applicationInfo.publicSourceDir = mPluginFile.getPath();
        }
        if (applicationInfo.dataDir == null) {
            applicationInfo.dataDir = PluginDirHelper.getPluginDataDir(mHostContext, applicationInfo.packageName);
        }
        if (applicationInfo.nativeLibraryDir == null) {
            applicationInfo.nativeLibraryDir = PluginDirHelper.getPluginNativeLibraryDir(mHostContext, applicationInfo.packageName);
        }
        if (TextUtils.isEmpty(applicationInfo.processName)) {
            applicationInfo.processName = applicationInfo.packageName;
        }
        return applicationInfo;
    }

    /**
     * 根据目标组件名从PMS缓存的组件信息中查找组件的ActivityInfo
     *
     * @param targetComponent 目标组件名
     * @param flags           标志位
     * @return 组件的ActivityInfo
     */
    public ActivityInfo getActivityInfo(ComponentName targetComponent, int flags) throws Exception {
        /**
         * 根据目标组件名从mActivityInfoCache中查找组件的ActivityInfo
         */
        ActivityInfo activityInfo = null;
        for (ComponentName componentName : mActivityInfoCache.keySet()) {
            if (componentName.getPackageName().equals(targetComponent.getPackageName()) &&
                    componentName.getClassName().equals(targetComponent.getClassName())) {
                activityInfo = mActivityInfoCache.get(componentName);
                break;
            }
        }
        if (activityInfo != null) {
            fixApplicationInfo(activityInfo.applicationInfo);
            //插件的进程名统一设置为它的包名
            if (TextUtils.isEmpty(activityInfo.processName)) {
                activityInfo.processName = activityInfo.packageName;
            }
        }
        return activityInfo;
    }

    /**
     * 根据标志位获取插件的ApplicationInfo
     *
     * @param flag 标志位，默认为0
     * @return 插件的ApplicationInfo
     */
    public ApplicationInfo getApplicationInfo(int flag) throws Exception {
        ApplicationInfo applicationInfo = mParser.generateApplicationInfo(flag);
        fixApplicationInfo(applicationInfo);
        if (TextUtils.isEmpty(applicationInfo.processName)) {
            applicationInfo.processName = applicationInfo.packageName;
        }
        return applicationInfo;
    }

    /**
     * 获取插件的包名
     *
     * @return 插件的包名
     */
    public String getmPackageName() {
        return mPackageName;
    }
}
